package edu.poly.game.control;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;

import edu.poly.game.battleship.BattleShip;
import edu.poly.game.battleship.BattleShipType;
import edu.poly.game.battleship.Orientation;
import edu.poly.game.battleship.impl.BattleShipFactory;
import edu.poly.game.config.Configuration;
import edu.poly.game.console.SystemConsole;
import edu.poly.game.player.Player;
import edu.poly.game.player.PlayerType;
import edu.poly.game.player.impl.PlayerFactory;
import edu.poly.game.util.Coordinate;
import edu.poly.game.util.Dimension;

public class GameSetupController {

	private static Pattern selectionPattern = Pattern.compile("^\\d{1}$");
	private static Pattern placePattern = Pattern.compile("^[1-9]+\\s{1}[1-9]+\\s[vVhH]{1}$");

	private List<Player> players;

	private static Random random = new Random();

	private Configuration config;

	public void init() {
		final GameMode mode = getGameMode();
		config = Configuration.getConfiguration(mode);
		players = new ArrayList<Player>();
		Player computer = PlayerFactory.getPlayer(PlayerType.COMPUTER, config.getBoardSize(), config.getNumberOfMissiles());
		Player human = PlayerFactory.getPlayer(PlayerType.HUMAN, config.getBoardSize(), config.getNumberOfMissiles());
		players.add(computer);
		players.add(human);

		for (Player player : players) {
			Set<BattleShipType> selectedShipTypes = new HashSet<BattleShipType>();
			for (int i = 0; i < config.getNumberOfShipsPerPlayer(); i++) {
				if (player.isAutomatic() == false) {
					BattleShipType shipType = getBattleShipType();
					while (selectedShipTypes.contains(shipType)) {
						SystemConsole.printMessage("You've already choosen this ship, please choose another one.");
						shipType = getBattleShipType();
					}
					selectedShipTypes.add(shipType);
					Dimension shipDimension = config.getShipConfig().get(shipType);
					BattleShip battleShip = getBattleShip(shipType, shipDimension);
					while (player.addBattleShip(battleShip) == false) {
						SystemConsole.printMessage("Your ship was not placed successfully.");
						battleShip = getBattleShip(shipType, shipDimension);
					}
					// prints player grid
					SystemConsole.printGameGrid(player.getGameBoard(), computer.getGameBoard());
				} else {
					// auto generate for computer player
					BattleShipType shipType = BattleShipType.getBattleShipType(i + 1);
					Dimension shipDimension = config.getShipConfig().get(shipType);
					BattleShip battleShip = autoGenerateBattleShip(shipType, shipDimension, config.getBoardSize());
					while (player.addBattleShip(battleShip) == false) {
						battleShip = autoGenerateBattleShip(shipType, shipDimension, config.getBoardSize());
					}
				}
			}
		}
	}

	public List<Player> getPlayers() {
		return this.players;
	}

	public Configuration getConfiguration() {
		return config;
	}

	private BattleShip autoGenerateBattleShip(final BattleShipType shipType, final Dimension shipDimension,
			final Dimension boardDimension) {
		int x = 0;
		int y = 0;
		Orientation orientation = null;
		if (shipDimension.getHeight() == boardDimension.getHeight()) {
			int which = random.nextInt(2);
			if (which == 0) {
				x = 0;
				y = random.nextInt(boardDimension.getHeight() - 1);
				orientation = Orientation.VERTICLE;
			} else {
				x = random.nextInt(boardDimension.getWidth() - 1);
				y = 0;
				orientation = Orientation.HORIZONTAL;
			}
		} else {
			x = random.nextInt(boardDimension.getWidth() - 1);
			y = random.nextInt(boardDimension.getHeight() - 1);
		}
		Coordinate fromCoordinate = new Coordinate(x, y);

		if (orientation == null) {
			int orientationInt = random.nextInt(2);
			if (orientationInt == 1) {
				orientation = Orientation.HORIZONTAL;
			} else {
				orientation = Orientation.VERTICLE;
			}
		}
		return BattleShipFactory.getBattleShip(fromCoordinate, shipType, orientation, shipDimension);
	}

	private BattleShip getBattleShip(final BattleShipType shipType, final Dimension shipDimension) {
		String placementInput = null;
		boolean reEnter = false;

		StringBuilder msg = new StringBuilder("Please enter coordinate and orientation for this ship, e.g. 1 5 V: ");

		BattleShip battleShip = null;
		do {
			placementInput = SystemConsole.askForInput(msg.toString());
			if (placePattern.matcher(placementInput).matches() == false) {
				reEnter = true;
			} else {
				String[] input = placementInput.split(" ");
				int x = Integer.parseInt(input[0]) - 1;
				int y = Integer.parseInt(input[1]) - 1;
				Coordinate fromCoordinate = new Coordinate(x, y);
				String orientationStr = input[2];
				Orientation orientation = Orientation.getOrientation(orientationStr);
				return BattleShipFactory.getBattleShip(fromCoordinate, shipType, orientation, shipDimension);
			}

			if (reEnter) {
				SystemConsole.printMessage("You've entered invalid ship placement coordinate.");
			}
		} while (reEnter);
		return battleShip;
	}

	private BattleShipType getBattleShipType() {
		String shipType = null;
		boolean reEnter = false;

		StringBuilder msg = new StringBuilder("Please Choose a Ship: ");
		for (BattleShipType type : BattleShipType.values()) {
			msg.append(" ").append(type.getOrdinal()).append(". ").append(type.getName());
		}

		BattleShipType ship = null;
		do {
			shipType = SystemConsole.askForInput(msg.toString());
			if (selectionPattern.matcher(shipType).matches() == false) {
				reEnter = true;
			} else {
				int shipOrdinal = Integer.parseInt(shipType);
				ship = BattleShipType.getBattleShipType(shipOrdinal);
				if (ship == null) {
					reEnter = true;
				} else {
					reEnter = false;
				}
			}

			if (reEnter) {
				SystemConsole.printMessage("You've selected invalide ship type.");
			}
		} while (reEnter);
		return ship;
	}

	private GameMode getGameMode() {
		String gameMode = null;
		boolean reEnter = false;

		StringBuilder msg = new StringBuilder("Please Choose Game Mode: ");
		for (GameMode mode : GameMode.values()) {
			msg.append(" ").append(mode.getOrdinal()).append(". ").append(mode.getName());
		}

		GameMode mode = null;
		do {
			gameMode = SystemConsole.askForInput(msg.toString());
			if (selectionPattern.matcher(gameMode).matches() == false) {
				reEnter = true;
			} else {
				int gameModeOrdinal = Integer.parseInt(gameMode);
				mode = GameMode.getGameMode(gameModeOrdinal);
				if (mode == null) {
					reEnter = true;
				} else {
					reEnter = false;
				}
			}

			if (reEnter) {
				SystemConsole.printMessage("You've selected invalide game mode.");
			}
		} while (reEnter);
		return mode;
	}
}
